package bvrtc

import (
	"fmt"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpfile"
	"gogs.smarteye.com/smarteye/gortc/pkgExt/rtpencode"
	"log"
	"time"

	"github.com/pion/rtcp"
	"github.com/pion/rtp"
	"github.com/yapingcat/gomedia/mpeg2"
	"gogs.smarteye.com/smarteye/gortc/pkg/format"
	"gogs.smarteye.com/smarteye/gortc/pkg/liberrors"
	"gogs.smarteye.com/smarteye/gortc/pkg/rtcpreceiver"
	"gogs.smarteye.com/smarteye/gortc/pkg/rtcpsender"
	"gogs.smarteye.com/smarteye/gortc/pkg/rtplossdetector"
	"gogs.smarteye.com/smarteye/gortc/pkg/rtpreorderer"
	"gogs.smarteye.com/smarteye/gortc/pkg/rtptime"
)

type Format struct {
	cm     *Media
	format format.Format

	reorderer    *rtpreorderer.Reorderer       // play
	lossDetector *rtplossdetector.LossDetector // play

	rtcpReceiver *rtcpreceiver.RTCPReceiver // play
	rtcpSender   *rtcpsender.RTCPSender     // record or back channel

	rtpTimeEncoder    *rtptime.Encoder
	rtpEncoderDecoder *rtpencode.RtpEncoderDecoder
}

func newFormat(cm *Media, f format.Format) *Format {
	var initialTimestamp uint32 = 0

	cf := &Format{
		cm:                cm,
		format:            f,
		reorderer:         rtpreorderer.New(),
		lossDetector:      rtplossdetector.New(),
		rtpEncoderDecoder: rtpencode.NewRtpEncoderDecoder(f),
		rtpTimeEncoder: &rtptime.Encoder{
			ClockRate:        f.ClockRate(),
			InitialTimestamp: &initialTimestamp,
		},
	}

	pc := cm.pc

	if cm.isLocal() {
		// local
		if cm.HaveSender() {
			log.Println("local rtcp sender")
			cf.rtcpSender = rtcpsender.New(
				cf.format.ClockRate(),
				time.Second*5,
				time.Now,
				func(pkt rtcp.Packet) {
					_ = pc.WritePacketRTCP(cf.cm.media, cf.format, pkt)
				},
			)
		}
	} else {
		// remote
		if cm.HaveSender() {
			log.Println("remote rtcp receiver")
			cf.rtcpReceiver, _ = rtcpreceiver.New(
				f.ClockRate(),
				&cm.media.SSRC,
				time.Second*5,
				time.Now,
				func(packet rtcp.Packet) error {
					return pc.WritePacketRTCP(cm.media, cf.format, packet)
				},
			)
		}
	}

	return cf
}

func (cf *Format) close() {
	if v := cf.rtcpSender; v != nil {
		v.Close()
	}

	if v := cf.rtcpReceiver; v != nil {
		v.Close()
	}
}

func (cf *Format) WritePacketRTP(pkt *rtp.Packet, ntp time.Time) error {
	if cf.rtcpSender == nil {
		return fmt.Errorf("rtcp sender not set")
	}

	pc := cf.cm.pc
	pkt.PayloadType = cf.format.PayloadType()

	byts := make([]byte, 1472)
	n, err := pkt.MarshalTo(byts)
	if err != nil {
		return err
	}
	byts = byts[:n]

	cf.rtcpSender.ProcessPacket(pkt, ntp, cf.format.PTSEqualsDTS(pkt))

	return pc.write(byts)
}

func (cf *Format) SendFileEOF() error {
	cm := cf.cm
	pc := cm.pc

	eof := &rtpfile.App{
		Source: cm.media.SSRC,
		Name:   [4]byte{'e', 'o', 'f', ' '},
		App:    make([]byte, 8),
	}

	log.Println("发送EOF")
	return pc.WritePacketRTCP(cm.media, cf.format, eof)
}

func (cf *Format) SendBye(reason string) error {
	cm := cf.cm
	pc := cm.pc

	eof := &rtcp.Goodbye{
		Sources: []uint32{cm.media.SSRC},
		Reason:  reason,
	}

	log.Println("发送BYE")
	return pc.WritePacketRTCP(cm.media, cf.format, eof)
}

func (cf *Format) Format() format.Format {
	return cf.format
}

func (cf *Format) readPacket(packet *rtp.Packet) {
	pc := cf.cm.pc
	h := pc.handle

	packets, lost := cf.reorderer.Process(packet)
	if lost != 0 {
		h.OnPacketLost(liberrors.ErrClientRTPPacketsLost{Lost: lost})
		// do not return
	}

	now := time.Now()

	for _, pkt := range packets {
		err := cf.rtcpReceiver.ProcessPacket(pkt, now, cf.format.PTSEqualsDTS(pkt))
		if err != nil {
			h.OnDecodeError(err)
			continue
		}

		pts, _ := pc.packetPTS(cf.format, pkt)
		ntp, _ := cf.rtcpReceiver.PacketNTP(pkt.Timestamp)

		h.OnPacketRTP(&Packet{
			Media:  cf.cm,
			Format: cf,
			RTP:    pkt,
			PTS:    pts,
			NTP:    ntp,
		})

	}
}

// ---------------------------------------------------------------------------------------------

func (cf *Format) CreateEncoder() error {
	return cf.rtpEncoderDecoder.CreateEncoder()
}

func (cf *Format) CreateDecoder() error {
	return cf.rtpEncoderDecoder.CreateDecoder()
}

func (cf *Format) Encode(data [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
	packets, err := cf.rtpEncoderDecoder.Encode(data)

	for _, packet := range packets {
		packet.Timestamp = cf.rtpTimeEncoder.Encode(pts)
	}

	return packets, err
}

func (cf *Format) Decode(pkt *rtp.Packet) ([][]byte, error) {
	return cf.rtpEncoderDecoder.Decode(pkt)
}

func (cf *Format) EncodePS(data []byte, cid mpeg2.PS_STREAM_TYPE, pts time.Duration) ([]*rtp.Packet, error) {
	packets, err := cf.rtpEncoderDecoder.EncodePS(data, cid, pts)

	for _, packet := range packets {
		packet.Timestamp = cf.rtpTimeEncoder.Encode(pts)
	}

	return packets, err
}

func (cf *Format) DecodePS(pkt *rtp.Packet, onFrame func(data []byte, cid mpeg2.PS_STREAM_TYPE, pts time.Duration)) error {
	return cf.rtpEncoderDecoder.DecodePS(pkt, onFrame)
}

// ---------------------------------------------------------------------------------------------
